--------------------------------------------------------------------------------
-- This package is a thin Ada binding to the GNU Multiple Precision Library.
-- See <http://gmplib.org/>.
-- It simply translates in Ada most of the content of the C header file : gmp.h
--
-- Copyright (C) 2009 Vincent DIEMUNSCH.
-- GNU General Public License.
--
-- This program is free software: you can redistribute it and/or modify it under
-- the terms of the GNU General Public License as published by the Free Software
-- Foundation, either version 3 of the License, or (at your option) any later
-- version.
--
-- This file is distributed in the hope that it will be useful, but WITHOUT ANY
-- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
-- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License along with
-- this program.  If not, see <http://www.gnu.org/licenses/>.
--------------------------------------------------------------------------------
--
-- IMPLEMENTATION NOTES :
--    1. THE CURRENT IMPLEMENTATION IS NOT EXHAUSTIVE, refer to gmp manual to
--       add new functions.
--    2. THE SIZE OF TYPES CORRESPONDS TO A 32 bits x86 running GCC on Darwin
--       (MAC OS X), Linux or MS-Windows.

with Interfaces.C;         use Interfaces.C;
with Interfaces.C.Strings; use Interfaces.C.Strings;

-- Non portable type definitions :
with MP_x86_32bits;
with MP_x86_64bits;

package GMP is


   GNU_MP_Version            : constant := 4;
   GNU_MP_Version_Minor      : constant := 3;
   GNU_MP_Version_PatchLevel : constant := 1;


   -----------------------------
   -- Select your hardware !! --
   -----------------------------
   package Specific renames MP_x86_64bits;

   -----------------
   -- Basic types --
   -----------------
   subtype Base_Arg is int range -36 .. 62;
   -- " The base argument may vary from 2 to 62 or from 2 to 36 ".
   --   (And in some functions also from -2 to -62) !!

   type mpz_t is private; -- Integers Numbers.
   type mpq_t is private; -- Rational Numbers.
   type mpf_t is private; -- Floating Point Numbers.

   type mp_limb_a    is private;                   -- for mpfr !
   type mp_exp_t_ptr is access Specific.mp_exp_t;

    --------------------------------------
    -- Unbounded Signed Integer Numbers --
    --------------------------------------

   -- 5.1 INITIALIZATION :
   procedure mpz_init  (integer : in out mpz_t);
   procedure mpz_init2 (integer : in out mpz_t; n : unsigned_long);
   -- Initialize Integer, with space for n bits, and set its value to 0.

   procedure mpz_clear (integer : in out mpz_t);
   -- Free the space occupied by integer.
   -- Call this function for all mpz_t variables when you are done with them.

   procedure mpz_realloc2 (integer : in out mpz_t; n : unsigned_long);
   -- Change the space allocated for integer to n bits. The value in integer is
   -- preserved if it fits, or is set to 0 if not. This function can be used to
   -- increase the space for a variable in order to avoid repeated automatic
   -- reallocations, or to decrease it to give memory back to the heap.


   -- 5.2 ASSIGNEMENT :
   procedure mpz_set     (rop : in out mpz_t; op : mpz_t);
   procedure mpz_set_ui  (rop : in out mpz_t; op : unsigned_long);
   procedure mpz_set_si  (rop : in out mpz_t; op : long);
   procedure mpz_set_d   (rop : in out mpz_t; op : double);
   procedure mpz_set_q   (rop : in out mpz_t; op : mpq_t);
   procedure mpz_set_f   (rop : in out mpz_t; op : mpf_t);
   -- set the value of rop from op.
   -- mpz_set_d, mpz_set_q and mpz_set_f truncate op to make it an integer.

   procedure mpz_set_str (fn_result : out int; rop : in out mpz_t; str : Chars_Ptr; base : Base_Arg);
   -- Set the value of rop from str, a null-terminated C string in base base.
   -- White space is allowed in the string, and is simply ignored.
   -- This function returns 0 if the entire string is a valid number in base base. Otherwise it returns −1.

   procedure mpz_swap (rop1, rop2 : in out mpz_t);
   -- Swap the values rop1 and rop2 efficiently.


   -- 5.3 COMBINED INITIALIZATION AND ASSIGNEMENT :
   -- Warning : Do not call for already initialized variables !
   procedure mpz_init_set    (rop : in out mpz_t; op : mpz_t);
   procedure mpz_init_set_ui (rop : in out mpz_t; op : unsigned_long);
   procedure mpz_init_set_si (rop : in out mpz_t; op : long);
   procedure mpz_init_set_d  (rop : in out mpz_t; op : double);
   -- Initialize rop with limb space and set the initial numeric value from op.

   procedure mpz_init_set_str (fn_result : out int; rop : in out mpz_t; str : Chars_Ptr; base : Base_Arg);
   -- Initialize rop and set its value like mpz_set_str (see its documentation for details).
   -- In C, mpz_init_set_str is a function that returns an int value. But in Ada, it needs to be a procedure
   -- since rop is modified by the call, and the return value is :
   -- If the string is a correct base base number, the function returns 0; if an error occurs it returns 1.
   -- rop is initialized even if an error occurs. (I.e., you have to call mpz_clear for it.)
   -- NOTE that the following function declaration, also it is false since rop is indeed modified, still
   -- works !!
   --function mpz_init_set_str (rop : mpz_t; str : C.Char_Array; base : C.int) return C.int;

   -- 5.4 CONVERSION :
   function mpz_get_ui (op : mpz_t) return unsigned_long;
   -- Return the value of op as an unsigned long. If op is too big to fit an
   -- unsigned long then just the least significant bits that do fit are
   -- returned. The sign of op is ignored, only the absolute value is used.
   function mpz_get_si (op : mpz_t) return long;
   -- If op fits into a signed long int return the value of op. Otherwise return
   -- the least significant part of op, with the same sign as op. If op is too
   -- big to fit in a signed long int, the returned result is probably not very
   -- useful. To find out if the value will fit, use the function mpz_fits_slong_p.
   function mpz_get_d (op : mpz_t) return double;
   -- Convert op to a double, truncating if necessary (ie. rounding towards zero).
   -- If the exponent from the conversion is too big, the result is system
   -- dependent. An infinity is returned where available. A hardware overflow
   -- trap may or may not occur.
   function mpz_get_d_2exp (exp : access long; op : mpz_t) return double;
   -- Convert op to a double, truncating if necessary (ie. rounding towards zero),
   -- and returning the exponent separately.
   -- The return value is in the range 0.5 <= |d| < 1 and the exponent is stored
   -- to *exp. d*2**exp is the (truncated) op value. If op is zero, the return
   -- is 0.0 and 0 is stored to *exp.
   -- This is similar to the standard C frexp function.
   -- WARNING : NEED TO BE TESTED : especialy the access parameter in Ada !

   function mpz_get_str (str : chars_ptr; base : Base_Arg; Op : mpz_t) return chars_ptr;
   -- Convert op to a string of digits in base base. The base argument may vary
   -- from 2 to 62 or from 2 to 36. For base in the range 2..36, digits and
   -- upper-case letters are used; for 37..62, digits, upper-case letters, and
   -- lower-case letters (in that significance order) are used.
   -- If str is NULL, the result string is allocated using the current allocation
   -- function. The block will be strlen(str)+1 bytes, that being exactly enough
   -- for the string and null-terminator.
   -- If str is not NULL, it should point to a block of storage large enough for
   -- the result, that being mpz_sizeinbase (op, base) + 2. The two extra bytes
   -- are for a possible minus sign, and the null-terminator.
   -- A pointer to the result string is returned, being either the allocated
   -- block, or the given str.



   -- 5.5 ARITHMETIC :
   procedure mpz_add    (rop : in out mpz_t; op1, op2 : mpz_t);
   procedure mpz_add_ui (rop : in out mpz_t; op1 : mpz_t; op2 : unsigned_long);
   -- set rop to op1 + op2.

   procedure mpz_sub    (rop : in out mpz_t; op1, op2 : mpz_t);
   procedure mpz_sub_ui (rop : in out mpz_t; op1 : mpz_t; op2 : unsigned_long);
   procedure mpz_ui_sub (rop : in out mpz_t; op1 : unsigned_long; op2 : mpz_t);
   -- set rop to op1 - op2.

   procedure mpz_mul    (rop : in out mpz_t; op1, op2 : mpz_t);
   procedure mpz_mul_si (rop : in out mpz_t; op1 : mpz_t; op2 : long);
   procedure mpz_mul_ui (rop : in out mpz_t; op1 : mpz_t; op2 : unsigned_long);
   -- set rop to op1 * op2.

   procedure mpz_addmul    (rop : in out mpz_t; op1, op2 : mpz_t);
   procedure mpz_addmul_ui (rop : in out mpz_t; op1 : mpz_t; op2 : unsigned_long);
   -- set rop to rop + op1 * op2.

   procedure mpz_submul    (rop : in out mpz_t; op1, op2 : mpz_t);
   procedure mpz_submul_ui (rop : in out mpz_t; op1 : mpz_t; op2 : unsigned_long);
   -- set rop to rop - op1 * op2.

   procedure mpz_mul_2exp (rop : in out mpz_t; op1 : mpz_t; op2 : unsigned_long);
   -- Set rop to op1 ◊ 2op2. This operation can also be defined as a left shift by op2 bits.

   procedure mpz_neg (rop : in out mpz_t; op1 : mpz_t);   -- Set rop to -op.
   procedure mpz_abs (rop : in out mpz_t; op1 : mpz_t);   -- Set rop to abs(op).

   -- 5.6 DIVISION :
   procedure mpz_cdiv_q  (q   : in out mpz_t; n, d : mpz_t);
   procedure mpz_fdiv_q  (q   : in out mpz_t; n, d : mpz_t);
   procedure mpz_tdiv_q  (q   : in out mpz_t; n, d : mpz_t);

   procedure mpz_cdiv_r  (r   : in out mpz_t; n, d : mpz_t);
   procedure mpz_fdiv_r  (r   : in out mpz_t; n, d : mpz_t);
   procedure mpz_tdiv_r  (r   : in out mpz_t; n, d : mpz_t);

   procedure mpz_cdiv_qr (q,r : in out mpz_t; n, d : mpz_t);
   procedure mpz_fdiv_qr (q,r : in out mpz_t; n, d : mpz_t);
   procedure mpz_tdiv_qr (q,r : in out mpz_t; n, d : mpz_t);

   -- Warning, these "functions" modify the parameters q, r : mpz_t !
   function mpz_cdiv_q_ui (q  : mpz_t; n : mpz_t; d : unsigned_long) return unsigned_long;
   function mpz_fdiv_q_ui (q  : mpz_t; n : mpz_t; d : unsigned_long) return unsigned_long;
   function mpz_tdiv_q_ui (q  : mpz_t; n : mpz_t; d : unsigned_long) return unsigned_long;

   function mpz_cdiv_r_ui (r  : mpz_t; n : mpz_t; d : unsigned_long) return unsigned_long;
   function mpz_fdiv_r_ui (r  : mpz_t; n : mpz_t; d : unsigned_long) return unsigned_long;
   function mpz_tdiv_r_ui (r  : mpz_t; n : mpz_t; d : unsigned_long) return unsigned_long;

   function mpz_cdiv_qr_ui(q,r: mpz_t; n : mpz_t; d : unsigned_long) return unsigned_long;
   function mpz_fdiv_qr_ui(q,r: mpz_t; n : mpz_t; d : unsigned_long) return unsigned_long;
   function mpz_tdiv_qr_ui(q,r: mpz_t; n : mpz_t; d : unsigned_long) return unsigned_long;



   function  mpz_cdiv_ui (n : mpz_t; d : unsigned_long) return unsigned_long;
   function  mpz_fdiv_ui (n : mpz_t; d : unsigned_long) return unsigned_long;
   function  mpz_tdiv_ui (n : mpz_t; d : unsigned_long) return unsigned_long;

   procedure mpz_cdiv_q_2exp (q  : in out mpz_t; n : mpz_t; b : unsigned_long);
   procedure mpz_fdiv_q_2exp (q  : in out mpz_t; n : mpz_t; b : unsigned_long);
   procedure mpz_tdiv_q_2exp (q  : in out mpz_t; n : mpz_t; b : unsigned_long);

   procedure mpz_cdiv_r_2exp (r  : in out mpz_t; n : mpz_t; b : unsigned_long);
   procedure mpz_fdiv_r_2exp (r  : in out mpz_t; n : mpz_t; b : unsigned_long);
   procedure mpz_tdiv_r_2exp (r  : in out mpz_t; n : mpz_t; b : unsigned_long);

   -- Divide n by d, forming a quotient q and/or remainder r.
   -- For the 2exp functions, d = 2b. The rounding is in three styles, each
   -- suiting different applications :
   -- "cdiv" : rounds q up towards +Inf, and r will have the opposite sign to d.
   --          The c stands for "ceil".
   -- "fdiv" : rounds q down towards -Inf, and r will have the same sign as d.
   --          The f stands for "floor".
   -- "tdiv" : rounds q towards zero, and r will have the same sign as n. The t
   --          stands for "truncate".

   -- In all cases q and r will satisfy n = qd + r, and r will satisfy 0 <= |r| < |d|.
   -- Note that for qr the same variable cannot be passed for both q and r, or
   -- results will be unpredictable.
   -- For the ui variants the return value is the remainder, and in fact returning the remainder is
   -- all the div_ui functions do. For tdiv and cdiv the remainder can be negative, so for those
   -- the return value is the absolute value of the remainder.
   -- For the 2exp variants the divisor is 2b. These functions are implemented as right shifts and
   -- bit masks, but of course they round the same as the other functions.
   -- For positive n both mpz_fdiv_q_2exp and mpz_tdiv_q_2exp are simple bitwise right shifts.
   -- For negative n, mpz_fdiv_q_2exp is effectively an arithmetic right shift treating n as twos
   -- complement the same as the bitwise logical functions do, whereas mpz_tdiv_q_2exp effectively
   -- treats n as sign and magnitude.

   procedure mpz_mod    (r : in out mpz_t; n,d : mpz_t);
   function  mpz_mod_ui (r :        mpz_t; n   : mpz_t; d : unsigned_long) return unsigned_long;
   --  Set r to n mod d. The sign of the divisor is ignored; the result is always non-negative.
   --  mpz_mod_ui is identical to mpz_fdiv_r_ui above, returning the remainder as well as setting
   --  r. See mpz_fdiv_ui above if only the return value is wanted.

   procedure mpz_divexact    (q : in out mpz_t; n,d : mpz_t);
   procedure mpz_divexact_ui (q : in out mpz_t; n   : mpz_t; d : unsigned_long);
   -- Set q to n/d. These functions produce correct results only when it is
   -- known in advance that d divides n.
   -- These routines are much faster than the other division functions, and are the best choice
   --  when exact division is known to occur, for example reducing a rational to lowest terms.

   function mpz_divisible_p      (n : mpz_t; d : mpz_t)         return int;
   function mpz_divisible_ui_p   (n : mpz_t; d : unsigned_long) return int;
   function mpz_divisible_2exp_p (n : mpz_t; b : unsigned_long) return int;
   --  Return non-zero if n is exactly divisible by d, or in the case of mpz_divisible_2exp_p by 2b.
   --  n is divisible by d if there exists an integer q satisfying n = qd. Unlike the other division
   --  functions, d = 0 is accepted and following the rule it can be seen that only 0 is considered
   --  divisible by 0.

   function mpz_congruent_p      (n, c, d : mpz_t)                       return int;
   function mpz_congruent_ui_p   (n       : mpz_t; c, d : unsigned_long) return int;
   function mpz_congruent_2exp_p (n, c    : mpz_t; b    : unsigned_long) return int;
   -- Return non-zero if n is congruent to c modulo d, or in the case of
   -- mpz_congruent_2exp_p modulo 2b.
   -- n is congruent to c mod d if there exists an integer q satisfying
   -- n = c +qd. Unlike the other division functions, d = 0 is accepted and
   -- following the rule it can be seen that n and c are considered congruent
   -- mod 0 only when exactly equal.


   -- 5.7 EXPONENTIATION :
   procedure mpz_powm    (rop : in out mpz_t; base, exp, modulo : in mpz_t);
   procedure mpz_powm_ui (rop : in out mpz_t; base, modulo : in mpz_t; exp : in unsigned_long);
   -- Set rop to baseexp mod modulo. Negative exp is supported if an inverse
   -- base^-1 mod modulo exists (See mpz_invert in section 5.9). If an inverse
   -- doesn't exist then a divide by zero is raised.

   procedure mpz_pow_ui    (rop : in out mpz_t; base : in mpz_t; exp : in unsigned_long);
   procedure mpz_ui_pow_ui (rop : in out mpz_t; base, exp : in unsigned_long);
   -- Set rop to base^exp. The case 00 yields 1.

   -- 5.8 ROOT EXTRACTION :
   procedure mpz_root (rop : in out mpz_t; op : mpz_t; n : unsigned_long);
   -- Set rop to the truncated integer part of the nth root of op.
   -- Note : in C it is a function that return non-zero if the computation was
   --        exact, i.e., if op is rop to the nth power.
   procedure mpz_rootrem (root, rmndr : in out mpz_t; u : mpz_t; n : unsigned_long);
   -- Set root  to the truncated integer part of the nth root of u.
   -- Set rmndr to the remainder, (u - root**n)
   procedure mpz_sqrt (rop : in out mpz_t; op : mpz_t);
   -- Set rop to the truncated integer part of the square root of op.
   procedure mpz_sqrtrem (sqrt, rmndr : in out mpz_t; op : mpz_t);
   -- Set sqrt  to the truncated integer part of the square root of op, like mpz_sqrt.
   -- Set rmndr to the remainder (op - sqrt**2), which will be zero if op is a
   -- perfect square.
   -- WARNING : If sqrt and rmndr are the same variable, the results are undefined.
   function mpz_perfect_power_p (op : mpz_t) return int;
   -- Return non-zero if op is a perfect power, i.e., if there exist integers a
   -- and b, with b > 1, such that op = ab. Under this definition both 0 and 1
   -- are considered to be perfect powers. Negative values of op are accepted,
   -- but of course can only be odd perfect powers.
   function mpz_perfect_square_p (op : mpz_t) return int;
   -- Return non-zero if op is a perfect square, i.e., if the square root of op
   -- is an integer. Under this definition both 0 and 1 are considered to be
   -- perfect squares.

   -- 5.9 NUMBER THEORETIC :
   function mpz_probab_prime_p (n : mpz_t; reps : int) return int;
   -- Determine whether n is prime. Return 2 if n is definitely prime, return 1
   -- if n is probably prime (without being certain), or return 0 if n is
   -- definitely composite. This function does some trial divisions, then some
   -- Miller-Rabin probabilistic primality tests. reps controls how many such
   -- tests are done, 5 to 10 is a reasonable number, more will reduce the chances
   -- of a composite being returned as "probably prime". Miller-Rabin and similar
   -- tests can be more properly called compositeness tests. Numbers which fail
   -- are known to be composite but those which pass might be prime or might be
   -- composite. Only a few composites pass, hence those which pass are considered
   -- probably prime.
   procedure mpz_nextprime (rop : in out mpz_t; op : in mpz_t);
   -- Set rop to the next prime greater than op. This function uses a probabilistic
   -- algorithm to identify primes. For practical purposes it’s adequate, the
   -- chance of a composite passing will be extremely small.
   procedure mpz_gcd (rop : in out mpz_t; op1, op2 : in mpz_t);
   -- Set rop to the greatest common divisor of op1 and op2. The result is always
   -- positive even if one or both input operands are negative.
   function mpz_gcd_ui (rop, op1 : mpz_t; op2 : unsigned_long) return unsigned_long;
   -- Compute the greatest common divisor of op1 and op2. If rop is not NULL,
   -- store the result there. If the result is small enough to fit in an unsigned
   -- long int, it is returned. If the result does not fit, 0 is returned, and
   -- the result is equal to the argument op1. Note that the result will always
   -- fit if op2 is non-zero.
   -- WARNING : in Ada we souldn't use rop since it is a function !
   procedure mpz_gcdext (g,s,t : in out mpz_t; a,b : in mpz_t);
   -- Set g to the greatest common divisor of a and b, and in addition set s and
   -- t to coefficients satisfying as + bt = g. g is always positive, even if one
   -- or both of a and b are negative. The value of s and t are choosen such that
   -- |s| =< |b| and |t| =< |a|. If t is NULL then that value is not computed.
   procedure mpz_lcm    (rop : in out mpz_t; op1, op2 : in mpz_t);
   procedure mpz_lcm_ui (rop : in out mpz_t; op1 : mpz_t; op2 : unsigned_long);
   -- Set rop to the least common multiple of op1 and op2. rop is always positive,
   -- irrespective of the signs of op1 and op2. rop will be zero if either op1
   -- or op2 is zero.
   function  mpz_invert (rop : mpz_t; op1, op2 : mpz_t) return int;
   procedure mpz_invert (rop : in out mpz_t; op1, op2 : mpz_t);
   -- Compute the inverse of op1 modulo op2 and put the result in rop. If the
   -- inverse exists, the return value is non-zero and rop will satisfy
   -- 0 =< |rop| < |op2|. If the inverse doesn't exist the return value is zero
   -- and rop is undefined.
   -- WARNING : in ADA we may have troubles using rop if we use the function !
   function mpz_jacobi (a, b : mpz_t) return int;
   -- Calculate the Jacobi symbol (a/b). This is defined only for b odd.
   function mpz_legendre (a, p : mpz_t) return int;
   -- Calculate the Legendre symbol (a/p). This is defined only for p an odd
   -- positive prime, and for such p it's identical to the Jacobi symbol.
   function mpz_kronecker    (a, b : mpz_t)                 return int;
   function mpz_kronecker_si (a : mpz_t; b: long)           return int;
   function mpz_kronecker_ui (a : mpz_t; b: unsigned_long)  return int;
   function mpz_si_kronecker (a : long; b : mpz_t)          return int;
   function mpz_ui_kronecker (a : unsigned_long; b : mpz_t) return int;
   -- Calculate the Jacobi symbol (a/b), with the Kronecker extension (a/2) = (2/a)
   -- when a odd, or (a/2) = 0 when a even.
   -- When b is odd the Jacobi symbol and Kronecker symbol are identical, so
   -- mpz_kronecker_ui etc can be used for mixed precision Jacobi symbols too.
   -- For more information see Henri Cohen section 1.4.2 (see Appendix B),
   -- or any number theory textbook.
   procedure mpz_remove (rop : in out mpz_t; op, f : mpz_t);
   function  mpz_remove (rop, op, f : mpz_t) return unsigned_long;
   -- Remove all occurrences of the factor f from op and store the result in rop.
   -- The return value is how many such occurrences were removed.
   -- WARNING : the function call will modify rop although in Ada it shouldn't !!
   procedure mpz_fac_ui (rop : in out mpz_t; op : unsigned_long);
   -- Set rop to op!, the factorial of op.
   procedure mpz_bin_ui   (rop : in out mpz_t; n: mpz_t; k : unsigned_long);
   procedure mpz_bin_uiui (rop : in out mpz_t; n, k : unsigned_long);
   -- Compute the binomial coefficient (n k) and store the result in rop.
   -- Negative values of n are supported by mpz_bin_ui.
   procedure mpz_fib_ui  (fn         : in out mpz_t; n : unsigned_long);
   procedure mpz_fib2_ui (fn, fnsub1 : in out mpz_t; n : unsigned_long);
   -- mpz_fib_ui sets fn to to F(n), the n'th Fibonacci number. mpz_fib2_ui sets
   -- fn to F(n), and fnsub1 to F(n-1).
   -- These functions are designed for calculating isolated Fibonacci numbers.
   -- When a sequence of values is wanted it's best to start with mpz_fib2_ui
   -- and iterate the defining F(n+1) = F(n) + F(n-1) or similar.
   procedure mpz_lucnum_ui  (ln         : in out mpz_t; n : unsigned_long);
   procedure mpz_lucnum2_ui (ln, lnsub1 : in out mpz_t; n : unsigned_long);
   -- mpz_lucnum_ui sets ln L(n) the n'th Lucas number. mpz_lucnum2_ui sets ln
   -- to L(n), and lnsub1 to L(n-1).
   -- These functions are designed for calculating isolated Lucas numbers. When
   -- a sequence of values is wanted it's best to start with mpz_lucnum2_ui and
   -- iterate the defining L(n+1) = L(n) + L(n-1) or similar.
   -- The Fibonacci numbers and Lucas numbers are related sequences, so it's
   -- never necessary to call both mpz_fib2_ui and mpz_lucnum2_ui. The formulas
   -- for going from Fibonacci to Lucas can be found in Section 16.7.5 [Lucas
   -- Numbers Algorithm], page 107, the reverse is straightforward too.

   -- 5.10 COMPARISON :
   function mpz_cmp    (op1, op2 : mpz_t)                      return int;
   function mpz_cmp_si (op1      : mpz_t; op2 : long)          return int;
   function mpz_cmp_ui (op1      : mpz_t; op2 : unsigned_long) return int;
   function mpz_cmp_d  (op1      : mpz_t; op2 : double)        return int;
   -- Compare op1 and op2. Return a positive value if op1 > op2 ,
   -- zero if op1 = op2 , or a negative value if op1 < op2.
   -- NOTE : Although cmp_si and cmp_ui are supposed to be macros, they exist
   --        in fact as functions and the MACROS are only little optimisations
   --        for GCC.

   function mpz_cmpabs    (op1, op2 : mpz_t)                      return int;
   function mpz_cmpabs_d  (op1      : mpz_t; op2 : double)        return int;
   function mpz_cmpabs_ui (op1      : mpz_t; op2 : unsigned_long) return int;
   -- Compare the absolute values of op1 and op2. Return a positive value if
   -- |op1| > |op2|, zero if |op1| = |op2|, or a negative value if |op1| < |op2|.
   -- mpz_cmpabs_d can be called with an infinity, but results are undefined for
   -- a NaN.

   -- 5.11 LOGICAL AND BIT MANIPULATION :

   -- 5.12 INPUT ANT OUTPUT :

   -- 5.13 RANDOM NUMBER :

   -- 5.14 INTEGER IMPORT AND EXPORT :


   -- 5.15 MISCALLANEOUS :
   function mpz_fits_ulong_p  (op : mpz_t) return int;
   function mpz_fits_slong_p  (op : mpz_t) return int;
   function mpz_fits_uint_p   (op : mpz_t) return int;
   function mpz_fits_sint_p   (op : mpz_t) return int;
   function mpz_fits_ushort_p (op : mpz_t) return int;
   function mpz_fits_sshort_p (op : mpz_t) return int;
   -- Return non-zero iff the value of op fits in an unsigned long int,
   -- signed long int, unsigned int, signed int, unsigned short int, or signed
   -- short int, respectively. Otherwise, return zero.

   function mpz_odd_p  (op : mpz_t) return int;
   function mpz_even_p (op : mpz_t) return int;
   -- Determine whether op is odd or even, respectively. Return non-zero if yes,
   -- zero if no. These macros evaluate their argument more than once.

   function mpz_sizeinbase (op : mpz_t; base : int) return size_t;
   -- Return the size of op measured in number of digits in the given base. base
   -- can vary from 2 to 62. The sign of op is ignored, just the absolute value
   -- is used. The result will be either exact or 1 too big. If base is a power
   -- of 2, the result is always exact. If op is zero the return value is always 1.
   -- This function can be used to determine the space required when converting
   -- op to a string. The right amount of allocation is normally two more than
   -- the value returned by mpz_sizeinbase, one extra for a minus sign and one
   -- for the null-terminator.
   -- It will be noted that mpz_sizeinbase(op,2) can be used to locate the most
   -- significant 1 bit in op, counting from 1. (Unlike the bitwise functions
   -- which start from 0, See Section 5.11 LOGICAL AND BIT MANIPULATION FUNCTIONS).

   ----------------------
   -- Rational Numbers --
   ----------------------

   -- All rational arithmetic functions assume operands have a canonical form,
   -- and canonicalize their result. The canonical from means that the denominator
   -- and the numerator have no common factors, and that the denominator is
   -- positive. Zero has the unique representation 0/1.
   -- Pure assignment functions do not canonicalize the assigned variable.
   -- It is the responsibility of the user to canonicalize the assigned variable
   -- before any arithmetic operations are performed on that variable.

   procedure mpq_canonicalize (op : in out mpq_t);
   -- Remove any factors that are common to the numerator and denominator of op,
   -- and make the denominator positive.

   -- 6.1 INITIALIZATION AND ASSIGNEMENT :

   procedure mpq_init  (dest_rational   : out mpq_t);
   procedure mpq_clear (rational_number : in out mpq_t);
   -- Initialize dest_rational and set it to 0/1. Each variable should normally
   -- only be initialized once, or at least cleared out (using the function
   -- mpq_clear) between each initialization.
   -- Free the space occupied by rational number. Make sure to call this function
   -- for all mpq_t variables when you are done with them.

   procedure mpq_set   (rop : in out mpq_t; op : mpq_t);
   procedure mpq_set_z (rop : in out mpq_t; op : mpz_t);
   -- Assign rop from op.

   procedure mpq_set_ui (rop : in out mpq_t; op1, op2: unsigned_long);
   procedure mpq_set_si (rop : in out mpq_t; op1 : long; op2: unsigned_long);
   -- Set the value of rop to op1/op2. Note that if op1 and op2 have common
   -- factors, rop has to be passed to mpq_canonicalize before any operations
   -- are performed on rop.

   subtype q1_Base_Arg is int range 0 .. 62;
   procedure mpq_set_str (fn_result : out int; rop : in out mpq_t; str : chars_ptr; base : q1_Base_Arg);
   -- Set rop from a null-terminated string str in the given base. The string can
   -- be an integer like "41" or a fraction like "41/152". The fraction must be
   -- in canonical form or if not then mpq_canonicalize must be called.
   -- The numerator and optional denominator are parsed the same as in mpz_set_str
   -- The base can vary from 2 to 62, or if base is 0 then the leading characters
   -- are used: 0x or 0X for hex, 0b or 0B for binary, 0 for octal, or decimal
   -- otherwise.
   -- The return value is 0 if the entire string is a valid number, or −1 if not.

   procedure mpq_swap (rop1, rop2 : in out mpq_t);
   -- Swap the values rop1 and rop2 efficiently.


   -- 6.2 CONVERSION :

   function mpq_get_d (op : mpq_t) return double;
   -- Convert op to a double, truncating if necessary (ie. rounding towards zero).
   -- If the exponent from the conversion is too big or too small to fit a double
   -- then the result is system dependent. For too big an infinity is returned
   -- when available. For too small 0.0 is normally returned. Hardware overflow,
   -- underflow and denorm traps may or may not occur.

   procedure mpq_set_d (rop : in out mpq_t; op : double);
   procedure mpq_set_f (rop : in out mpq_t; op : mpf_t);
   -- Set rop to the value of op. There is no rounding, this conversion is exact.

   subtype q2_Base_Arg is int range 2 .. 36;
   function mpq_get_str (str : chars_ptr; base : q2_Base_Arg; op : mpq_t) return chars_ptr;
   -- Convert op to a string of digits in base base. The base may vary from 2 to 36.
   -- The string will be of the form "num/den", or if the denominator is 1 then
   -- just "num". If str is NULL, the result string is allocated using the current
   -- allocation function. The block will be strlen(str)+1 bytes, that being
   -- exactly enough for the string and null-terminator.
   -- If str is not NULL, it should point to a block of storage large enough for
   -- the result, that being mpz_sizeinbase (mpq_numref(op), base) + mpz_sizeinbase (mpq_denref(op), base) + 3
   -- The three extra bytes are for a possible minus sign, possible slash, and
   -- the null-terminator. A pointer to the result string is returned, being
   -- either the allocated block, or the given str.


   -- 6.3 ARITHMETIC :

   procedure mpq_add (sum : in out mpq_t; addend1, addend2 : mpq_t);
   -- Set sum to addend1 + addend2.
   procedure mpq_sub (difference : in out mpq_t; minuend, subtrahend : mpq_t);
   -- Set difference to minuend - subtrahend
   procedure mpq_mul (product : in out mpq_t; multiplier, multiplicand : mpq_t);
   -- Set product to multiplier * multiplicand
   procedure mpq_mul_2exp (rop : in out mpq_t; op1 : mpq_t; op2: unsigned_long);
   -- Set rop to op1 * 2**op2
   procedure mpq_div (quotient : in out mpq_t; dividend, divisor : mpq_t);
   -- Set quotient to dividend / divisor
   procedure mpq_div_2exp (rop : in out mpq_t; op1 : mpq_t; op2: unsigned_long);
   -- Set rop to op1 / 2**op2
   procedure mpq_neg (negated_operand : in out mpq_t; operand : mpq_t);
   -- Set negated operand to -operand.
   procedure mpq_abs (rop : in out mpq_t; op : mpq_t);
   -- Set rop to the absolute value of op.
   procedure mpq_inv (rop : in out mpq_t; op : mpq_t);
   -- Set inverted number to 1/number. If the new denominator is zero, this
   -- routine will divide by zero.


   -- 6.4 COMPARISON :

   function mpq_cmp (op1, op2 : mpq_t) return int;
   -- Compare op1 and op2. Return a positive value if op1 > op2, zero if op1 = op2,
   -- and a negative value if op1 < op2. To determine if two rationals are equal,
   -- mpq_equal is faster than mpq_cmp.

   function mpq_equal (op1, op2 : mpq_t) return int;
   -- Return non-zero if op1 and op2 are equal, zero if they are non-equal.
   -- Although mpq_cmp can be used for the same purpose, this function is much
   -- faster.

   -- 6.5 APPLYING INTEGER FUNCTIONS TO RATIONALS

   -- The set of mpq functions is quite small. In particular, there are few
   -- functions for either input or output. The following functions give direct
   -- access to the numerator and denominator of an mpq_t.
   -- Note that if an assignment to the numerator and/or denominator could take
   -- an mpq_t out of the canonical then mpq_canonicalize must be called before
   -- any other mpq functions are applied to that mpq_t.

   procedure mpq_get_num (numerator   : in out mpz_t; rational    : mpq_t);
   procedure mpq_get_den (denominator : in out mpz_t; rational    : mpq_t);
   procedure mpq_set_num (rational    : in out mpq_t; numerator   : mpz_t);
   procedure mpq_set_den (rational    : in out mpq_t; denominator : mpz_t);
   -- Get or set the numerator or denominator of a rational.


   -- 6.6 INPUTS AND OUTPUTS :

   -- NOTE : <stdio.h> must have been included before gmp.h during compilation
   --        to allow "gmp.h" to define prototypes for these functions..
   -- Passing a NULL pointer for a stream argument to any of these functions will
   -- make them read from stdin and write to stdout, respectively.

--   function mpq_out_str (FILE : stream_ptr; base : q2_Base_Arg; op : mpq_t) return size_t;
   -- Output op on stdio stream stream, as a string of digits in base base.

   -----------------------------------------------
   -- Multiple Precision Floating Point Numbers --
   -----------------------------------------------

   procedure mpf_set_default_prec (prec : unsigned_long);
   function  mpf_get_default_prec return unsigned_long;
   -- Set the default precision to be at least prec bits. All subsequent calls
   -- to mpf_init will use this precision, but previously initialized variables
   -- are unaffected.
   -- Return the default precision actually used.

   procedure mpf_init   (x : in out mpf_t);
   procedure mpf_init2  (x : in out mpf_t; prec : unsigned_long);
   -- Initialize x to 0, and set its precision to be at least prec bits.
																	-- Do not call if already initialized !
   procedure mpf_clear (x : in out mpf_t);
   -- Free the space occupied by x. Make sure to call this function for all
   -- mpf_t variables when you are done with them.

   -- Assignment : --
   procedure mpf_set    (rop : in out mpf_t; op : mpf_t);
   procedure mpf_set_ui (rop : in out mpf_t; op : unsigned_long);
   procedure mpf_set_d  (rop : in out mpf_t; op : double);
   procedure mpf_set_z  (rop : in out mpf_t; op : mpz_t);
   procedure mpf_set_q  (rop : in out mpf_t; op : mpq_t);
   -- Set the value of rop from op.

   procedure mpf_set_str (fn_result : out int; rop : in out mpf_t; str : Chars_Ptr; base : Base_Arg);
   -- Set the value of rop from the string in str. The string is of the form ‘M@N’ or, if the base is 10 or
   -- less, alternatively ‘MeN’. ‘M’ is the mantissa and ‘N’ is the exponent. The mantissa is always in the
   -- specified base. The exponent is either in the specified base or, if base is negative, in decimal. The
   -- decimal point expected is taken from the current locale, on systems providing localeconv. The argument
   -- base may be in the ranges 2 to 62, or −62 to −2. Negative values are used to specify that the exponent
   -- is in decimal. For bases up to 36, case is ignored; upper-case and lower-case letters have the same
   -- value; for bases 37 to 62, upper-case letter represent the usual 10..35 while lower-case letter
   -- represent 36..61.
   -- Unlike the corresponding mpz function, the base will not be determined from the leading characters of
   -- the string if base is 0. This is so that numbers like ‘0.23’ are not interpreted as octal.
   -- White space is allowed in the string, and is simply ignored.
   -- This function returns 0 if the entire string is a valid number in base base. Otherwise it returns −1.

   -- Init and assign : --
   procedure mpf_init_set    (rop : in out mpf_t; op : mpf_t);
   procedure mpf_init_set_si (rop : in out mpf_t; op : long);
   procedure mpf_init_set_d  (rop : in out mpf_t; op : double);
   -- Initialize rop and set its value from op.

   procedure mpf_init_set_str (fn_result : out int; rop : in out mpf_t; str : Chars_Ptr; base : Base_Arg);
   -- Initialize rop and set its value from the string in str. See "mpf_set_str".
   -- Note that rop is initialized even if an error occurs. (I.e., you have to call mpf_clear for it.)
   -- The precision of rop will be taken from the active default precision, as set by mpf_set_default_prec.

   function mpf_get_str	(str      : Chars_Ptr;
                         expptr   : mp_exp_t_ptr;
                         base     : Base_Arg;
                         n_digits : size_t;
                         op       : mpf_t) return Chars_Ptr;
   -- Convert op to a string of digits in base base. The base argument may vary from 2 to 62 or from
   -- 2 to 36. Up to n_digits digits will be generated. Trailing zeros are not returned.
   -- No more digits than can be accurately represented by op are ever generated. If n_digits is 0
   -- then that accurate maximum number of digits are generated.

   -- Arithmetic functions :
   procedure mpf_add    (rop : in out mpf_t; op1, op2 : in mpf_t);
   procedure mpf_add_ui (rop : in out mpf_t; op1 : mpf_t; op2 : unsigned_long);
   -- Set rop to op1 + op2 .

   procedure mpf_sub    (rop : in out mpf_t; op1, op2 : in mpf_t);
   procedure mpf_ui_sub (rop : in out mpf_t; op1 : unsigned_long; op2 : mpf_t);
   procedure mpf_sub_ui (rop : in out mpf_t; op1 : mpf_t; op2 : unsigned_long);
   -- Set rop to op1 - op2.

   procedure mpf_mul    (rop : in out mpf_t; op1, op2 : in mpf_t);
   procedure mpf_mul_ui (rop : in out mpf_t; op1 : mpf_t; op2 : unsigned_long);
   --Set rop to op1 * op2 .

   -- Division is undefined if the divisor is zero, and passing a zero divisor to the divide functions
   -- will make these functions intentionally divide by zero. This lets the user handle arithmetic exceptions
   -- in these functions in the same manner as other arithmetic exceptions.
   procedure mpf_div    (rop : in out mpf_t; op1, op2 : in mpf_t);
   procedure mpf_ui_div (rop : in out mpf_t; op1 : unsigned_long; op2 : mpf_t);
   procedure mpf_div_ui (rop : in out mpf_t; op1 : mpf_t; op2 : unsigned_long);
   -- Set rop to op1 / op2.

   procedure mpf_sqrt    (rop : in out mpf_t);
   procedure mpf_sqrt_ui (rop : in out mpf_t; op : unsigned_long);               -- Set rop to sqrt(op).
   procedure mpf_pow_ui  (rop : in out mpf_t; op1 : mpf_t; op2 : unsigned_long); -- Set rop to op1^op2.
   procedure mpf_neg     (rop : in out mpf_t; op  : in mpf_t);                   -- Set rop to -op.

   -- comparison functions :
   function mpf_cmp    (op1, op2 : mpf_t)             return int;
   function mpf_cmp_si (op1      : mpf_t; op2 : long) return int;
   -- Compare op1 and op2. Return a positive value if op1 > op2,
   -- zero if op1 = op2 , and a negative value if op1 < op2 .


   ----------------
   -- gmp_printf --
   ----------------

   procedure gmp_printf (str : Char_Array; integer : mpz_t);
   --	procedure gmp_printf (str : Char_Array; float   : mpf_t);


private-------------------------------------------------------------------------

   subtype mp_limb_t is Specific.mp_limb_t;
   type    mp_limb_a is access all mp_limb_t;

   -----------
   -- mpz_t --
   -----------

   type mpz_struct is record
      mp_alloc : int;       -- Number of *limbs* allocated and pointed to by the _mp_d field.
      mp_size  : int;       -- abs(_mp_size) is the number of limbs the last field points to.  If mp_size is
                            -- negative this is a negative number.
      mp_d     : mp_limb_a; -- Pointer to the limbs.
   end record;

   type mpz_t is array (1 .. 1) of mpz_struct;

   -----------
   -- mpq_t --
   -----------

   type mpq_struct is record
      mp_num, mp_den : mpz_struct;
   end record;

   type mpq_t is array (1..1) of mpq_struct;

   -----------
   -- mpf_t --
   -----------

   subtype mp_exp_t is Specific.mp_exp_t;

   type mpf_struct is record
      mp_prec : int;      -- Max precision, in number of `mp_limb_t's. Set by mpf_init and modified by mpf_set_prec.
                          -- The area pointed to by the mp_d field contains `prec' + 1 limbs.
      mp_size : int;      -- abs(mp_size) is the number of limbs the last field points to.
                          -- If mp_size is negative this is a negative number.
      mp_exp  : mp_exp_t; -- Exponent, in the base of `mp_limb_t'.
      mp_d    : mp_limb_a;-- Pointer to the limbs.
   end record;

   type mpf_t is array (1 .. 1) of mpf_struct;

   ----------------
   -- Convention --
   ----------------

   pragma Convention (C, mp_exp_t_ptr);

   pragma Convention (C, mp_limb_a);
   pragma Convention (C, mpz_struct);
   pragma Convention (C, mpz_t);

   pragma Convention (C, mpq_struct);
   pragma Convention (C, mpq_t);

   pragma Convention (C, mpf_struct);
   pragma Convention (C, mpf_t);

   ------------
   -- Import --
   ------------

   -- mpz --
   pragma Import (C, mpz_init, "__gmpz_init");
   pragma Import (C, mpz_init2, "__gmpz_init2");
   pragma Import (C, mpz_clear, "__gmpz_clear");
   pragma Import (C, mpz_realloc2, "__gmpz_realloc2");

   pragma Import (C, mpz_set, "__gmpz_set");
   pragma Import (C, mpz_set_ui, "__gmpz_set_ui");
   pragma Import (C, mpz_set_si, "__gmpz_set_si");
   pragma Import (C, mpz_set_d, "__gmpz_set_d");
   pragma Import (C, mpz_set_q, "__gmpz_set_q");
   pragma Import (C, mpz_set_f, "__gmpz_set_f");
   pragma Import (C, mpz_set_str, "__gmpz_set_str");
   pragma Import_Valued_Procedure (mpz_set_str);
   pragma Import (C, mpz_swap, "__gmpz_swap");

   pragma Import (C, mpz_init_set, "__gmpz_init_set");
   pragma Import (C, mpz_init_set_ui, "__gmpz_init_set_ui");
   pragma Import (C, mpz_init_set_si, "__gmpz_init_set_si");
   pragma Import (C, mpz_init_set_d, "__gmpz_init_set_d");
   pragma Import (C, mpz_init_set_str, "__gmpz_init_set_str");
   pragma Import_Valued_Procedure (mpz_init_set_str);

   pragma Import (C, mpz_get_ui, "__gmpz_get_ui");
   pragma Import (C, mpz_get_si, "__gmpz_get_si");
   pragma Import (C, mpz_get_d, "__gmpz_get_d");
   pragma Import (C, mpz_get_d_2exp, "__gmpz_get_d_2exp");
   pragma Import (C, mpz_get_str, "__gmpz_get_str");


   pragma Import (C, mpz_add, "__gmpz_add");
   pragma Import (C, mpz_add_ui, "__gmpz_add_ui");
   pragma Import (C, mpz_sub, "__gmpz_sub");
   pragma Import (C, mpz_sub_ui, "__gmpz_sub_ui");
   pragma Import (C, mpz_ui_sub, "__gmpz_ui_sub");
   pragma Import (C, mpz_mul, "__gmpz_mul");
   pragma Import (C, mpz_mul_si, "__gmpz_mul_si");
   pragma Import (C, mpz_mul_ui, "__gmpz_mul_ui");
   pragma Import (C, mpz_addmul, "__gmpz_addmul");
   pragma Import (C, mpz_addmul_ui, "__gmpz_addmul_ui");
   pragma Import (C, mpz_submul, "__gmpz_submul");
   pragma Import (C, mpz_submul_ui, "__gmpz_submul_ui");
   pragma Import (C, mpz_mul_2exp, "__gmpz_mul_2exp");
   pragma Import (C, mpz_neg, "__gmpz_neg");
   pragma Import (C, mpz_abs, "__gmpz_abs");

   pragma Import (C, mpz_cdiv_q, "__gmpz_cdiv_q");
   pragma Import (C, mpz_fdiv_q, "__gmpz_fdiv_q");
   pragma Import (C, mpz_tdiv_q, "__gmpz_tdiv_q");
   pragma Import (C, mpz_cdiv_r, "__gmpz_cdiv_r");
   pragma Import (C, mpz_fdiv_r, "__gmpz_fdiv_r");
   pragma Import (C, mpz_tdiv_r, "__gmpz_tdiv_r");
   pragma Import (C, mpz_cdiv_qr, "__gmpz_cdiv_qr");
   pragma Import (C, mpz_fdiv_qr, "__gmpz_fdiv_qr");
   pragma Import (C, mpz_tdiv_qr, "__gmpz_tdiv_qr");
   pragma Import (C, mpz_cdiv_q_ui, "__gmpz_cdiv_q_ui");
   pragma Import (C, mpz_fdiv_q_ui, "__gmpz_fdiv_q_ui");
   pragma Import (C, mpz_tdiv_q_ui, "__gmpz_tdiv_q_ui");
   pragma Import (C, mpz_cdiv_r_ui, "__gmpz_cdiv_r_ui");
   pragma Import (C, mpz_fdiv_r_ui, "__gmpz_fdiv_r_ui");
   pragma Import (C, mpz_tdiv_r_ui, "__gmpz_tdiv_r_ui");
   pragma Import (C, mpz_cdiv_qr_ui, "__gmpz_cdiv_qr_ui");
   pragma Import (C, mpz_fdiv_qr_ui, "__gmpz_fdiv_qr_ui");
   pragma Import (C, mpz_tdiv_qr_ui, "__gmpz_tdiv_qr_ui");
   pragma Import (C, mpz_cdiv_ui, "__gmpz_cdiv_ui");
   pragma Import (C, mpz_fdiv_ui, "__gmpz_fdiv_ui");
   pragma Import (C, mpz_tdiv_ui, "__gmpz_tdiv_ui");
   pragma Import (C, mpz_cdiv_q_2exp, "__gmpz_cdiv_q_2exp");
   pragma Import (C, mpz_fdiv_q_2exp, "__gmpz_fdiv_q_2exp");
   pragma Import (C, mpz_tdiv_q_2exp, "__gmpz_tdiv_q_2exp");
   pragma Import (C, mpz_cdiv_r_2exp, "__gmpz_cdiv_r_2exp");
   pragma Import (C, mpz_fdiv_r_2exp, "__gmpz_fdiv_r_2exp");
   pragma Import (C, mpz_tdiv_r_2exp, "__gmpz_tdiv_r_2exp");
   pragma Import (C, mpz_mod   , "__gmpz_mod");
   pragma Import (C, mpz_mod_ui, "__gmpz_mod_ui");
   pragma Import (C, mpz_divexact   , "__gmpz_divexact");
   pragma Import (C, mpz_divexact_ui, "__gmpz_divexact_ui");
   pragma Import (C, mpz_divisible_p, "__gmpz_divisible_p");
   pragma Import (C, mpz_divisible_ui_p  , "__gmpz_divisible_ui_p");
   pragma Import (C, mpz_divisible_2exp_p, "__gmpz_divisible_2exp_p");
   pragma Import (C, mpz_congruent_p     , "__gmpz_congruent_p");
   pragma Import (C, mpz_congruent_ui_p  , "__gmpz_congruent_ui_p");
   pragma Import (C, mpz_congruent_2exp_p, "__gmpz_congruent_2exp_p");

   pragma Import (C, mpz_powm, "__gmpz_powm");
   pragma Import (C, mpz_powm_ui, "__gmpz_powm_ui");
   pragma Import (C, mpz_pow_ui, "__gmpz_pow_ui");
   pragma Import (C, mpz_ui_pow_ui, "__gmpz_ui_pow_ui");

   pragma Import (C, mpz_root, "__gmpz_root");
   pragma Import (C, mpz_rootrem, "__gmpz_rootrem");
   pragma Import (C, mpz_sqrt, "__gmpz_sqrt");
   pragma Import (C, mpz_sqrtrem, "__gmpz_sqrtrem");
   pragma Import (C, mpz_perfect_power_p, "__gmpz_perfect_power_p");
   pragma Import (C, mpz_perfect_square_p, "__gmpz_perfect_square_p");

   pragma Import (C, mpz_probab_prime_p, "__gmpz_probab_prime_p");
   pragma Import (C, mpz_nextprime, "__gmpz_nextprime");
   pragma Import (C, mpz_gcd, "__gmpz_gcd");
   pragma Import (C, mpz_gcd_ui, "__gmpz_gcd_ui");
   pragma Import (C, mpz_gcdext, "__gmpz_gcdext");
   pragma Import (C, mpz_lcm, "__gmpz_lcm");
   pragma Import (C, mpz_lcm_ui, "__gmpz_lcm_ui");
   pragma Import (C, mpz_invert, "__gmpz_invert");
   pragma Import (C, mpz_jacobi, "__gmpz_jacobi");
   pragma Import (C, mpz_legendre, "__gmpz_legendre");
   pragma Import (C, mpz_kronecker, "__gmpz_kronecker");
   pragma Import (C, mpz_kronecker_si, "__gmpz_kronecker_si");
   pragma Import (C, mpz_kronecker_ui, "__gmpz_kronecker_ui");
   pragma Import (C, mpz_si_kronecker, "__gmpz_si_kronecker");
   pragma Import (C, mpz_ui_kronecker, "__gmpz_ui_kronecker");
   pragma Import (C, mpz_remove, "__gmpz_remove");
   pragma Import (C, mpz_fac_ui, "__gmpz_fac_ui");
   pragma Import (C, mpz_bin_ui, "__gmpz_bin_ui");
   pragma Import (C, mpz_bin_uiui, "__gmpz_bin_uiui");
   pragma Import (C, mpz_fib_ui, "__gmpz_fib_ui");
   pragma Import (C, mpz_fib2_ui, "__gmpz_fib2_ui");
   pragma Import (C, mpz_lucnum_ui, "__gmpz_lucnum_ui");
   pragma Import (C, mpz_lucnum2_ui, "__gmpz_lucnum2_ui");

   pragma Import (C, mpz_cmp, "__gmpz_cmp");
   pragma Import (C, mpz_cmp_si, "__gmpz_cmp_si");
   pragma Import (C, mpz_cmp_ui, "__gmpz_cmp_ui");
   pragma Import (C, mpz_cmp_d, "__gmpz_cmp_d");
   pragma Import (C, mpz_cmpabs, "__gmpz_cmpabs");
   pragma Import (C, mpz_cmpabs_d, "__gmpz_cmpabs_d");
   pragma Import (C, mpz_cmpabs_ui, "__gmpz_cmpabs_ui");

   pragma Import (C, mpz_fits_ulong_p, "__gmpz_fits_ulong_p");
   pragma Import (C, mpz_fits_slong_p, "__gmpz_fits_slong_p");
   pragma Import (C, mpz_fits_uint_p, "__gmpz_fits_uint_p");
   pragma Import (C, mpz_fits_sint_p, "__gmpz_fits_sint_p");
   pragma Import (C, mpz_fits_ushort_p, "__gmpz_fits_ushort_p");
   pragma Import (C, mpz_fits_sshort_p, "__gmpz_fits_sshort_p");
   pragma Import (C, mpz_odd_p,  "__gmpz_odd_p");
   pragma Import (C, mpz_even_p, "__gmpz_even_p");
   pragma Import (C, mpz_sizeinbase, "__gmpz_sizeinbase");

   -- mpq --
   pragma Import (C, mpq_canonicalize, "__gmpq_canonicalize");

   pragma Import (C, mpq_init, "__gmpq_init");
   pragma Import (C, mpq_clear, "__gmpq_clear");

   pragma Import (C, mpq_set, "__gmpq_set");
   pragma Import (C, mpq_set_z, "__gmpq_set_z");
   pragma Import (C, mpq_set_ui, "__gmpq_set_ui");
   pragma Import (C, mpq_set_si, "__gmpq_set_si");
   pragma Import (C, mpq_set_str, "__gmpq_set_str");
   pragma Import_Valued_Procedure (mpq_set_str);
   pragma Import (C, mpq_swap, "__gmpq_swap");

   pragma Import (C, mpq_get_d, "__gmpq_get_d");
   pragma Import (C, mpq_set_d, "__gmpq_set_d");
   pragma Import (C, mpq_set_f, "__gmpq_set_f");
   pragma Import (C, mpq_get_str, "__gmpq_get_str");

   pragma Import (C, mpq_add, "__gmpq_add");
   pragma Import (C, mpq_sub, "__gmpq_sub");
   pragma Import (C, mpq_mul, "__gmpq_mul");
   pragma Import (C, mpq_mul_2exp, "__gmpq_mul_2exp");
   pragma Import (C, mpq_div, "__gmpq_div");
   pragma Import (C, mpq_div_2exp, "__gmpq_div_2exp");
   pragma Import (C, mpq_neg, "__gmpq_neg");
   pragma Import (C, mpq_abs, "__gmpq_abs");
   pragma Import (C, mpq_inv, "__gmpq_inv");

   pragma Import (C, mpq_cmp, "__gmpq_cmp");
   pragma Import (C, mpq_equal, "__gmpq_equal");

   pragma Import (C, mpq_get_num, "__gmpq_get_num");
   pragma Import (C, mpq_get_den, "__gmpq_get_den");
   pragma Import (C, mpq_set_num, "__gmpq_set_num");
   pragma Import (C, mpq_set_den, "__gmpq_set_den");


   -- mpf --
   pragma Import (C, mpf_set_default_prec, "__gmpf_set_default_prec");
   pragma Import (C, mpf_get_default_prec, "__gmpf_get_default_prec");
   pragma Import (C, mpf_init, "__gmpf_init");
   pragma Import (C, mpf_init2, "__gmpf_init2");
   pragma Import (C, mpf_clear, "__gmpf_clear");

   pragma Import (C, mpf_set, "__gmpf_set");
   pragma Import (C, mpf_set_ui, "__gmpf_set_ui");
   pragma Import (C, mpf_set_d, "__gmpf_set_d");
   pragma Import (C, mpf_set_z, "__gmpf_set_z");
   pragma Import (C, mpf_set_q, "__gmpf_set_q");
   pragma Import (C, mpf_set_str, "__gmpf_set_str");
   pragma Import_Valued_Procedure (mpf_set_str);
   pragma Import (C, mpf_init_set, "__gmpf_init_set");
   pragma Import (C, mpf_init_set_d, "__gmpf_init_set_d");
   pragma Import (C, mpf_init_set_si, "__gmpf_init_set_si");
   pragma Import (C, mpf_init_set_str, "__gmpf_init_set_str");
   pragma Import_Valued_Procedure (mpf_init_set_str);
   pragma Import (C, mpf_get_str, "__gmpf_get_str");

   pragma Import (C, mpf_add, "__gmpf_add");
   pragma Import (C, mpf_add_ui, "__gmpf_add_ui");

   pragma Import (C, mpf_sub, "__gmpf_sub");
   pragma Import (C, mpf_ui_sub, "__gmpf_ui_sub");
   pragma Import (C, mpf_sub_ui, "__gmpf_sub_ui");

   pragma Import (C, mpf_mul, "__gmpf_mul");
   pragma Import (C, mpf_mul_ui, "__gmpf_mul_ui");
   pragma Import (C, mpf_div, "__gmpf_div");
   pragma Import (C, mpf_ui_div, "__gmpf_ui_div");
   pragma Import (C, mpf_div_ui, "__gmpf_div_ui");
   pragma Import (C, mpf_sqrt, "__gmpf_sqrt");
   pragma Import (C, mpf_sqrt_ui, "__gmpf_sqrt_ui");
   pragma Import (C, mpf_pow_ui, "__gmpf_pow_ui");
   pragma Import (C, mpf_neg, "__gmpf_neg");

   pragma Import (C, mpf_cmp, "__gmpf_cmp");
   pragma Import (C, mpf_cmp_si, "__gmpf_cmp_si");

   -- gmp printf --
   pragma Import (C, gmp_printf, "__gmp_printf");

end GMP;
